using System;
using System.Collections.Generic;
using System.ComponentModel;
using Microsoft.Ccr.Core;
using Microsoft.Dss.Core;
using Microsoft.Dss.Core.Attributes;
using Microsoft.Dss.ServiceModel.Dssp;
using Microsoft.Dss.ServiceModel.DsspServiceBase;
using W3C.Soap;
using submgr = Microsoft.Dss.Services.SubscriptionManager;
using ds = Microsoft.Dss.Services.Directory;
using referee = HCC.Robotics.HCCRefereeModule.Proxy;
using Microsoft.Dss.Core.DsspHttp;
using System.Threading;
using HCC.Robotics.HCCRefereeModule.Proxy;

namespace HCC.Robotics.HCCPlayerModule
{
    [Contract(Contract.Identifier)]
    [DisplayName("HCCPlayerModule")]
    [Description("HCCPlayerModule service")]
    class HCCPlayerModuleService : DsspServiceBase
    {
        [ServiceState(StateTransform = "HCC.Robotics.HCCPlayerModule.Resources.Transform.xslt")]
        [InitialStatePartner(Optional = true, ServiceUri = "HCCPlayerModule.Config.xml")]
        HCCPlayerModuleState _state = new HCCPlayerModuleState();

        [ServicePort("/HCCPlayerModule", AllowMultipleInstances = true)]
        HCCPlayerModuleOperations _mainPort = new HCCPlayerModuleOperations();

        [SubscriptionManagerPartner]
        submgr.SubscriptionManagerPort _submgrPort = new submgr.SubscriptionManagerPort();

        private Port<DateTime> _timerPort = new Port<DateTime>();

        referee.HCCRefereeModuleOperations _refereePort;
        referee.HCCRefereeModuleOperations _refereeNotify = new HCCRefereeModuleOperations();
        Port<Shutdown> _refereeShutdown = new Port<Shutdown>();

        private bool FoundReferee = false;

        public HCCPlayerModuleService(DsspServiceCreationPort creationPort)
            : base(creationPort)
        {
        }

        protected override void Start()
        {
            if (_state == null)
            {
                _state = new HCCPlayerModuleState();
                _state.Configuration = new RefereeConfig();
                _state.Configuration.Host = "localhost";
                _state.Configuration.Port = 60000;
                _state.Configuration.TeamColor = Team.Yellow;
            }
            SaveState(_state);

            //LogInfo(LogGroups.Console, "Sleep for 5 seconds to give the referee the change to start");
            //Thread.Sleep(5000);

            LogInfo(LogGroups.Activation, "Searching for Referee ...");
            SpawnIterator(FindReferee);
            while (!FoundReferee) ;
            LogInfo(LogGroups.Console, "Found Referee ...");
            SpawnIterator(SetupInterleaf);

            base.Start();
        }

        private IEnumerator<ITask> FindReferee()
        {
            UriBuilder builder = new UriBuilder(Schemes.DsspTcp, _state.Configuration.Host, _state.Configuration.Port + 1, ServicePaths.InstanceDirectory);

            builder.Scheme = new Uri(ServiceInfo.Service).Scheme;
            builder.Host = _state.Configuration.Host;

            ds.DirectoryPort port = ServiceForwarder<ds.DirectoryPort>(builder.Uri);
            ds.Get get = new ds.Get();
            ServiceInfoType[] list = null;
            port.Post(get);
            yield return Arbiter.Choice(get.ResponsePort,
                delegate(ds.GetResponseType response)
                {
                    list = response.RecordList;
                },
                delegate(Fault fault)
                {
                    list = new ServiceInfoType[0];
                    LogError(LogGroups.Activation, fault.ToException());
                    LogInfo(LogGroups.Activation, "No DSSNode found on destination.");
                    StartFailed();
                }
            );

            // dsshostnode found, now look for referee service.
            LogInfo(LogGroups.Console, "DSSNode found on destination.");
            foreach (ServiceInfoType type in list)
            {
                if (referee.Contract.Identifier == type.Contract)
                {
                    Uri addr = new Uri(type.Service);
                    _refereePort = ServiceForwarder<referee.HCCRefereeModuleOperations>(addr);
                    FoundReferee = true;
                    break;
                }
            }
            if (!FoundReferee)
            {
                list = new ServiceInfoType[0];
                LogError(LogGroups.Activation, "No Referee Service found on given Host and Port.");
                StartFailed();
                yield break;
            }

            LogInfo(LogGroups.Console, "Referee found on destination.");
            referee.Subscribe sub = new referee.Subscribe();
            sub.NotificationPort = _refereeNotify;
            sub.NotificationShutdownPort = _refereeShutdown;

            LogInfo(LogGroups.Console, "Subscribing!!!!.");
            _refereePort.Post(sub);
            LogInfo(LogGroups.Console, "Waiting for subscribe response.");
            Arbiter.Choice(sub.ResponsePort,
                delegate(SubscribeResponseType response)
                {
                    LogInfo(LogGroups.Activation, "Successfully Subscribed to: " + response.SubscriptionManager);
                },
                delegate(Fault fault)
                {
                    LogError(LogGroups.Activation, "No Referee Service found on given Host and Port.", fault);
                    StartFailed();
                }
            );
            LogInfo(LogGroups.Console, "Response received.");

            yield break;
        }

        private IEnumerator<ITask> SetupInterleaf()
        {
            referee.SetColorRequest req = new referee.SetColorRequest(_state.Configuration.TeamColor);
            yield return Arbiter.Choice(_refereePort.SetColor(req),
                delegate(DefaultUpdateResponseType success)
                {
                    LogInfo(LogGroups.PartnerManagement, "Player is set ...");
                },
                delegate(Fault f)
                {
                    LogError(LogGroups.Activation, "Referee already have the player color.");
                    _refereeShutdown.Post(new Shutdown());
                    StartFailed();
                }
            );

            MainPortInterleave.CombineWith(new Interleave(
                new TeardownReceiverGroup(),
                new ExclusiveReceiverGroup
                    (
                        Arbiter.Receive<referee.GameDataUpdate>(true, _refereeNotify, GameUpdateHandler)
                    ),
                new ConcurrentReceiverGroup()
            ));

            yield break;
        }

        private void GameUpdateHandler(referee.GameDataUpdate update)
        {
            if (update.Body.Data.Ball != null)
            {
                BallPosition ball = new BallPosition();
                ball.X = update.Body.Data.Ball.Ball.X;
                ball.Y = update.Body.Data.Ball.Ball.Y;
                BallPositionUpdate ballupdate = new BallPositionUpdate(ball);
                _mainPort.Post(ballupdate);
            }

            if (update.Body.Data.FieldData != null)
            {
                FieldDataClass fielddata = update.Body.Data.FieldData;
                FieldDataUpdate updatefield = new FieldDataUpdate(fielddata);
                _mainPort.Post(updatefield);
            }

            if (update.Body.Data.State != null)
            {
                StateDataClass gamestatedata = new StateDataClass();
                if (_state.Configuration.TeamColor == Team.Blue)
                {
                    gamestatedata.Player1Active = update.Body.Data.State.BluePlayer1Active;
                    gamestatedata.Player2Active = update.Body.Data.State.BluePlayer2Active;
                    gamestatedata.Oponent1Active = update.Body.Data.State.YellowPlayer1Active;
                    gamestatedata.Oponent2Active = update.Body.Data.State.YellowPlayer2Active;
                    // ID: 359
                    if (update.Body.Data.Players.IsFirstHalf)
                    {
                        gamestatedata.ScoreInRightGoal = false;
                    }
                    else
                    {
                        gamestatedata.ScoreInRightGoal = true;
                    }
                    // ID: 359
                }
                else
                {
                    gamestatedata.Player1Active = update.Body.Data.State.YellowPlayer1Active;
                    gamestatedata.Player2Active = update.Body.Data.State.YellowPlayer2Active;
                    gamestatedata.Oponent1Active = update.Body.Data.State.BluePlayer1Active;
                    gamestatedata.Oponent2Active = update.Body.Data.State.BluePlayer2Active;
                    // ID: 359
                    if (update.Body.Data.Players.IsFirstHalf)
                    {
                        gamestatedata.ScoreInRightGoal = true;
                    }
                    else
                    {
                        gamestatedata.ScoreInRightGoal = false;
                    }
                    // ID: 359
                }
                StateDataUpdate updateState = new StateDataUpdate(gamestatedata);
                _mainPort.Post(updateState);
            }

            if (update.Body.Data.Players != null)
            {
                if (_state.Configuration.TeamColor == Team.Blue)
                {
                    PlayersPositions positions = new PlayersPositions();
                    positions.Player1 = new PlayerPosition();
                    positions.Player1.X = update.Body.Data.Players.BluePlayer1.X;
                    positions.Player1.Y = update.Body.Data.Players.BluePlayer1.Y;
                    positions.Player1.Heading = update.Body.Data.Players.BluePlayer1Heading;
                    positions.Player2 = new PlayerPosition();
                    positions.Player2.X = update.Body.Data.Players.BluePlayer2.X;
                    positions.Player2.Y = update.Body.Data.Players.BluePlayer2.Y;
                    positions.Player2.Heading = update.Body.Data.Players.BluePlayer2Heading;
                    positions.Oponent1 = new PlayerPosition();
                    positions.Oponent1.X = update.Body.Data.Players.YellowPlayer1.X;
                    positions.Oponent1.Y = update.Body.Data.Players.YellowPlayer1.Y;
                    positions.Oponent1.Heading = update.Body.Data.Players.YellowPlayer1Heading;
                    positions.Oponent2 = new PlayerPosition();
                    positions.Oponent2.X = update.Body.Data.Players.YellowPlayer2.X;
                    positions.Oponent2.Y = update.Body.Data.Players.YellowPlayer2.Y;
                    positions.Oponent2.Heading = update.Body.Data.Players.YellowPlayer2Heading;

                    PlayersPositionsUpdate updateplayers = new PlayersPositionsUpdate(positions);
                    _mainPort.Post(updateplayers);
                    update.ResponsePort.Post(DefaultUpdateResponseType.Instance);
                }
                else
                {
                    PlayersPositions positions = new PlayersPositions();
                    positions.Player1 = new PlayerPosition();
                    positions.Player1.X = update.Body.Data.Players.YellowPlayer1.X;
                    positions.Player1.Y = update.Body.Data.Players.YellowPlayer1.Y;
                    positions.Player1.Heading = update.Body.Data.Players.YellowPlayer1Heading;
                    positions.Player2 = new PlayerPosition();
                    positions.Player2.X = update.Body.Data.Players.YellowPlayer2.X;
                    positions.Player2.Y = update.Body.Data.Players.YellowPlayer2.Y;
                    positions.Player2.Heading = update.Body.Data.Players.YellowPlayer2Heading;
                    positions.Oponent1 = new PlayerPosition();
                    positions.Oponent1.X = update.Body.Data.Players.BluePlayer1.X;
                    positions.Oponent1.Y = update.Body.Data.Players.BluePlayer1.Y;
                    positions.Oponent1.Heading = update.Body.Data.Players.BluePlayer1Heading;
                    positions.Oponent2 = new PlayerPosition();
                    positions.Oponent2.X = update.Body.Data.Players.BluePlayer2.X;
                    positions.Oponent2.Y = update.Body.Data.Players.BluePlayer2.Y;
                    positions.Oponent2.Heading = update.Body.Data.Players.BluePlayer2Heading;
                    PlayersPositionsUpdate updateplayers = new PlayersPositionsUpdate(positions);
                    _mainPort.Post(updateplayers);
                    update.ResponsePort.Post(DefaultUpdateResponseType.Instance);
                }
            }
        }

        [ServiceHandler]
        public void SubscribeHandler(Subscribe subscribe)
        {
            SubscribeHelper(_submgrPort, subscribe.Body, subscribe.ResponsePort);
        }

        [ServiceHandler]
        public void BallPositionUpdateHandler(BallPositionUpdate update)
        {
            SendNotification(_submgrPort, update);
            update.ResponsePort.Post(DefaultUpdateResponseType.Instance);
        }

        [ServiceHandler]
        public void PlayersPositionsUpdateHandler(PlayersPositionsUpdate update)
        {
            SendNotification(_submgrPort, update);
            update.ResponsePort.Post(DefaultUpdateResponseType.Instance);
        }

        [ServiceHandler]
        public void FieldDataUpdateHandler(FieldDataUpdate update)
        {
            SendNotification(_submgrPort, update);
            update.ResponsePort.Post(DefaultUpdateResponseType.Instance);
        }

        [ServiceHandler]
        public void StateDataUpdateHandler(StateDataUpdate update)
        {
            SendNotification(_submgrPort, update);
            update.ResponsePort.Post(DefaultUpdateResponseType.Instance);
        }
    }
}


